﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WarriorRush.GameDynamics.Characters;

namespace WarriorRush.GameDynamics
{
    public class CharacterClass
    {
        public CharacterTypes Type;
        public CharacterStats Stats;
        public string Name;
        public string Description;
        public ArenaSquare Square
        {
            set
            {
                square = value;
                if (value != null && value.Character != this)
                {
                    value.Character = this;
                }
                
            }
            get { return square; }
        }

        public GameEffect Boost;
        public GameEffect Drain;
        public SpecialAbility OffensiveAbility;
        public SpecialAbility DefensiveAbility;
        private ArenaSquare square;
        private GameEffect pendingBoost;
        private GameEffect pendingDrain;
        public bool IsDead { get { return Stats.Health <= 0; } }


        

        public CharacterClass(CharacterTypes type,CharacterStats newStats, SpecialAbility offense, SpecialAbility defense)
        {
            Type = type;
            Stats = newStats;
            OffensiveAbility = offense;
            offense.AbilityOwner = this;
            DefensiveAbility = defense;
            defense.AbilityOwner = this;
            Drain = new GameEffect(false);
            Boost = new GameEffect(true);
        }


        public virtual CharacterClass GetInstance(ArenaSquare position)
        {
            return new CharacterClass(
                    this.Type,
                    Stats.GetInstance(),
                    OffensiveAbility.GetInstance(OffensiveAbility.Power),
                    DefensiveAbility.GetInstance(DefensiveAbility.Power)
                    ) 
                    { 
                        Square = position,
                        Boost = new GameEffect(true),
                        Drain = new GameEffect(false),
                        Name = this.Name,
                        Description = this.Description
                        
                    
                    };
        }

        public void DealDefensiveDamage(CharacterClass damageTarget)
        {
            damageTarget.TakeDamage(
                Drain.ModifyDamageDealt(
                    Boost.ModifyDamageDealt(
                        Stats.Defense
                    )
                )
            );

        }

        public void DealOffensiveDamage(CharacterClass damageTarget)
        {

            damageTarget.TakeDamage(
                Drain.ModifyDamageDealt(
                    Boost.ModifyDamageDealt(
                        Stats.Attack
                    )
                )
            );
        }

        public void TakeDamage(int damage)
        {
           var totalDamage = 
                   Drain.ModifyDamageTaken(
                    Boost.ModifyDamageTaken(
                       damage
                    )
                );
            if(totalDamage > 0)
                Stats.Health -= totalDamage;
        }

        public void TakeEffects(GameEffect boost, GameEffect drain )
        {
            //We  are adding a new effect, and it is equal to or m ore powerful than pending effects
            //Rule: last equally or more powerful effect on the ability stack sticks
            
            var needNewBoost = boost != null &&( pendingBoost == null || boost.Power >= pendingBoost.Power);
            var needNewDrain = drain != null && (pendingDrain == null || drain.Power >= pendingDrain.Power);
            var newBoost = needNewBoost ? 
                                    Drain.ModifyBoostTaken(Boost.ModifyBoostTaken(boost.GetInstance(boost.Power))) : 
                                    pendingBoost;
            var newDrain = needNewDrain ? 
                                    Drain.ModifyBoostTaken(Drain.ModifyBoostTaken(drain.GetInstance(drain.Power))) :
                                    pendingDrain;
          

            pendingBoost = newBoost;
            pendingDrain = newDrain;
        }

        public virtual void Rush(CharacterClass rushTarget, Arena arena,List<CharacterClass> additionalTargets)
        {
            
            if(!OffensiveAbility.ReplacesAttack )
                DealOffensiveDamage(rushTarget);
            rushTarget.DealDefensiveDamage(this);
            OffensiveAbility.Execute(rushTarget, arena, additionalTargets);
            if (!rushTarget.IsDead && !rushTarget.DefensiveAbility.RequiresAddtionalTarget)
                rushTarget.DefensiveAbility.Execute(this, arena, null);
        }

       

        public virtual void DiscardUsedEffects()
        {
          
            Drain.RevertStats(this);
            Drain = new GameEffect(false);        
            Boost.RevertStats(this);
            Boost = new GameEffect(true);

        }

        public void ApplyNewEffects()
        {
           
            //If there is an incoming effect we need to do the following
            //Revert the stats of any lingering effects
            //Set the new effect
            //Do any modification with the new effect
            if (pendingBoost != null && pendingBoost.Power >= Boost.Power)
            {
                Boost.RevertStats(this);
                Boost = pendingBoost;
                Boost.ModifyStats(this);
            }
            if (pendingDrain != null && pendingDrain.Power >= Drain.Power)
            {
                Drain.RevertStats(this);
                Drain = pendingDrain;
                Drain.ModifyStats(this);
            }
            pendingBoost = null;
            pendingDrain = null;
        }

        public virtual List<SimplePoint> GetRushRange()
        {
            var targets = new List<SimplePoint> { new SimplePoint(1,0)};
            for (var n = 1; n <= Stats.RushRange; n++)
            {
                targets.Add(new SimplePoint(1, n));
                targets.Add(new SimplePoint(1, -n));
            }
            return targets;
        }
       
    }
}
